package ace.cmp.spring.data.jdbc.querydsl.autoconfig;

import ace.cmp.spring.data.jdbc.querydsl.core.LogicDeleteHelper;
import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslJdbcRepositoryExtendFactoryInjectHelperImpl;
import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslLogicNotDeleteFlagValueGetter;
import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslRelationHelper;
import ace.cmp.spring.data.jdbc.querydsl.core.converter.PageConverter;
import ace.cmp.spring.data.jdbc.querydsl.core.converter.impl.PageConverterImpl;
import ace.cmp.spring.data.jdbc.querydsl.core.impl.LogicDeleteHelperImpl;
import ace.cmp.spring.data.jdbc.querydsl.core.impl.QuerydslLogicNotDeleteFlagValueGetterImpl;
import ace.cmp.spring.data.jdbc.querydsl.core.impl.QuerydslRelationHelperImpl;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.*;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.impl.*;
import ace.cmp.spring.data.jdbc.querydsl.core.util.QuerydslAnnotationUtils;
import ace.cmp.spring.data.jdbc.querydsl.core.util.QuerydslPropertyRelationUtils;
import ace.cmp.spring.data.jdbc.querydsl.core.util.impl.QuerydslAnnotationUtilsImpl;
import ace.cmp.spring.data.jdbc.querydsl.core.util.impl.QuerydslPropertyRelationUtilsImpl;
import com.querydsl.sql.SQLQueryFactory;
import com.querydsl.sql.SQLTemplates;
import com.querydsl.sql.SQLTemplatesRegistry;
import com.querydsl.sql.spring.SpringConnectionProvider;
import com.querydsl.sql.spring.SpringExceptionTranslator;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Optional;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.support.SQLExceptionTranslator;

/**
 * @author caspar
 * @date 2023/4/3 15:59
 */
@Configuration
public class SpringDataJdbcQuerydslAutoConfig {
  @ConditionalOnMissingBean
  @Bean
  public PageConverter pageConverter() {
    return PageConverterImpl.INSTANCE;
  }

  @ConditionalOnMissingBean
  @Bean
  public SQLTemplates sqlTemplates(DataSource dataSource) throws SQLException {
    var sqlTemplatesRegistry = new SQLTemplatesRegistry();
    DatabaseMetaData metaData;
    try (var connection = dataSource.getConnection()) {
      metaData = connection.getMetaData();
    }

    return sqlTemplatesRegistry.getTemplates(metaData);
  }

  @ConditionalOnMissingBean
  @Bean
  public SpringExceptionTranslator springExceptionTranslator(
      Optional<SQLExceptionTranslator> sqlExceptionTranslator) {
    return sqlExceptionTranslator
        .map(SpringExceptionTranslator::new)
        .orElseGet(SpringExceptionTranslator::new);
  }

  @ConditionalOnMissingBean
  @Bean
  public com.querydsl.sql.Configuration querydslSqlConfiguration(SQLTemplates sqlTemplates) {
    var configuration = new com.querydsl.sql.Configuration(sqlTemplates);
    configuration.setExceptionTranslator(new SpringExceptionTranslator());
    return configuration;
  }

  @ConditionalOnMissingBean
  @Bean
  public SQLQueryFactory sqlQueryFactory(
      com.querydsl.sql.Configuration querydslSqlConfiguration, DataSource dataSource) {
    return new SQLQueryFactory(querydslSqlConfiguration, new SpringConnectionProvider(dataSource));
  }

  @ConditionalOnMissingBean
  @Bean
  public LogicDeleteHelper logicDeleteHelper(ApplicationContext applicationContext) {
    return new LogicDeleteHelperImpl(applicationContext);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslRelationHelper querydslRelationHelper(
      RelationalMappingContext mappingContext,
      ApplicationContext applicationContext,
      QuerydslLogicNotDeleteFlagValueGetter querydslLogicNotDeleteFlagValueGetter,
      LogicDeleteHelper logicDeleteHelper) {
    return new QuerydslRelationHelperImpl(
        mappingContext,
        applicationContext,
        querydslLogicNotDeleteFlagValueGetter,
        logicDeleteHelper);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslLogicNotDeleteFlagValueGetter querydslLogicNotDeleteFlagValueGetter(
      LogicDeleteHelper logicDeleteHelper) {
    return new QuerydslLogicNotDeleteFlagValueGetterImpl(logicDeleteHelper);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslNowGenerator querydslNowGenerator() {
    return new QuerydslNowGeneratorImpl();
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslCreatedDateAnnotationHandler querydslCreatedDateAnnotationHandler(
      QuerydslNowGenerator querydslNowGenerator,
      QuerydslPropertyRelationUtils querydslPropertyRelationUtils) {
    return new QuerydslCreatedDateAnnotationHandlerImpl(
        querydslNowGenerator, querydslPropertyRelationUtils);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslLastModifiedDateAnnotationHandler querydslLastModifiedDateAnnotationHandler(
      QuerydslNowGenerator querydslNowGenerator,
      QuerydslPropertyRelationUtils querydslPropertyRelationUtils) {
    return new QuerydslLastModifiedDateAnnotationHandlerImpl(
        querydslNowGenerator, querydslPropertyRelationUtils);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslVersionGenerator querydslVersionGenerator() {
    return new QuerydslVersionGeneratorImpl();
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslVersionAnnotationHandler querydslVersionAnnotationHandler(
      QuerydslPropertyRelationUtils querydslPropertyRelationUtils,
      QuerydslVersionGenerator querydslVersionGenerator) {
    return new QuerydslVersionAnnotationHandlerImpl(
        querydslPropertyRelationUtils, querydslVersionGenerator);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslPropertyRelationUtils querydslPropertyRelationUtils(
      QuerydslRelationHelper querydslRelationHelper) {
    return new QuerydslPropertyRelationUtilsImpl(querydslRelationHelper);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslJdbcRepositoryExtendFactoryInjectHelperImpl
      querydslJdbcRepositoryExtendFactoryInjectHelper(
          QuerydslRelationHelper querydslRelationHelper,
          QuerydslCreatedDateAnnotationHandler querydslCreatedDateAnnotationHandler,
          QuerydslLastModifiedDateAnnotationHandler querydslLastModifiedDateAnnotationHandler,
          QuerydslVersionAnnotationHandler querydslVersionAnnotationHandler,
          QuerydslNowGenerator querydslNowGenerator,
          QuerydslVersionGenerator querydslVersionGenerator,
          QuerydslLogicDeleteAnnotationHandler querydslLogicDeleteAnnotationHandler,
          QuerydslAnnotationUtils querydslAnnotationUtils) {
    return new QuerydslJdbcRepositoryExtendFactoryInjectHelperImpl(
        querydslRelationHelper,
        querydslCreatedDateAnnotationHandler,
        querydslLastModifiedDateAnnotationHandler,
        querydslVersionAnnotationHandler,
        querydslNowGenerator,
        querydslVersionGenerator,
        querydslLogicDeleteAnnotationHandler,
        querydslAnnotationUtils);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslAnnotationUtils querydslAnnotationUtils() {
    return new QuerydslAnnotationUtilsImpl();
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslLogicDeleteGenerator querydslLogicDeleteGenerator(
      QuerydslRelationHelper querydslRelationHelper) {
    return new QuerydslLogicDeleteGeneratorImpl(querydslRelationHelper);
  }

  @ConditionalOnMissingBean
  @Bean
  public QuerydslLogicDeleteAnnotationHandler querydslLogicDeleteAnnotationHandler(
      QuerydslPropertyRelationUtils querydslPropertyRelationUtils,
      QuerydslLogicDeleteGenerator querydslLogicDeleteGenerator) {
    return new QuerydslLogicDeleteAnnotationHandlerImpl(
        querydslPropertyRelationUtils, querydslLogicDeleteGenerator);
  }
}
